package rwt.server.railroad;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.*;

import org.slf4j.*;

import rwt.server.RailServer;
import rwt.server.core.*;

/**
 * Manages connected web clients.
 */
public class ClientManager implements IClientManager
{
	private static final Logger logger = LoggerFactory.getLogger(ClientManager.class);
	
	private static boolean isCreated = false;
	public static ClientManager create()
	{
		if (isCreated)
			throw new RuntimeException("ClientManager has already been created.");
		isCreated = true;
		return new ClientManager();
	}
	
	private int nextClientId = 1;
	private final Map<Integer, IClient> clients = new ConcurrentHashMap<>();
	private ExecutorService threadPool = Executors.newCachedThreadPool();
	
	private ClientManager()
	{
	}
	
	public synchronized int getNextClientId()
	{
		return nextClientId++;
	}
	
	public WebClient createClient(User user)
	{
		return new WebClient(this, getNextClientId(), user);
	}
	
	@Override
	public void addClient(IClient client)
	{
		clients.put(client.getId(), client);
		logger.info("ADDED connection (User: {}) [{}]", client.getUser().getUserName(), clients.size());
	}
	
	@Override
	public void removeClient(IClient client)
	{
		boolean mustSendAvailableTrains = false;
		for (Train train : RailServer.getTrainManager().getTrains())
		{
			if (train.getThrottleClientId() == client.getId())
			{
				train.closeThrottle();
				mustSendAvailableTrains = true;
			}
		}
		
		Switches.getInstance().removeClient(client);
		clients.remove(client.getId());
		logger.info("LOST connection (User: {}) [{}]", client.getUser().getUserName(), clients.size());
		
		if (mustSendAvailableTrains)
			sendToAll(RailServer.getTrainManager().getPutAvailableTrains());
	}
	
	public IClient getClientById(int clientId)
	{
		if (!clients.containsKey(clientId))
			throw new KeyNotFoundException("Client with ID " + clientId + " not found.");
		return clients.get(clientId);
	}
	
	@Override
	public void submitTask(Runnable runnable)
	{
		threadPool.submit(runnable);
	}
	
	/**
	 * Broadcasts the given message to all connected clients. The message object is translated into JSON only
	 * once.
	 * 
	 * @param message The message to broadcast.
	 */
	public void sendToAll(BaseMessage message)
	{
		logger.debug("Sending message (ID {}) to {} clients.", message.id, clients.size());
		try
		{
			String json = RailServer.getMessageAdaptor().messageToJson(message);
			sendToAll(json);
		}
		catch (IOException e)
		{
			logger.warn("", e);
		}
	}
	
	public void sendToAll(String text)
	{
		for (IClient client : clients.values())
			client.write(text);
	}
}
